package com.xncoding.freemarker.ctr;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.alibaba.fastjson.JSON;
import com.xncoding.freemarker.utile.FileUtil;
import com.xncoding.freemarker.utile.FileUtiles;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.activation.MimetypesFileTypeMap;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import org.apache.commons.io.IOUtils;
import org.springframework.web.multipart.MultipartHttpServletRequest;

@RequestMapping("/file")
@RestController
public class FileDealController {

    @RequestMapping("/api4")
    public void api4(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 告诉浏览器用什么软件可以打开此文件
        response.setHeader("content-Type", "application/vnd.ms-excel");
        // 下载文件的默认名称
        response.setHeader("Content-Disposition", "attachment;filename=111.xls");
    }
    @RequestMapping(value = "/getVideo")
    public void getVido(HttpServletResponse response) {
        String file = "C:\\Users\\Boss\\Desktop\\123.avi";
        try {
            FileInputStream inputStream = new FileInputStream(file);
            byte[] data = new byte[inputStream.available()];
            inputStream.read(data);
            String diskfilename = "final.avi";
            response.setContentType("video/avi");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + diskfilename + "\"");
            System.out.println("data.length " + data.length);
            response.setContentLength(data.length);
            response.setHeader("Content-Range", "" + Integer.valueOf(data.length - 1));
            response.setHeader("Accept-Ranges", "bytes");
            response.setHeader("Etag", "W/\"9767057-1323779115364\"");
            OutputStream os = response.getOutputStream();
            os.write(data);
            //先声明的流后关掉！
            os.flush();
            os.close();
            inputStream.close();

        } catch (Exception e) {
            e.printStackTrace();

        }
    }

    @RequestMapping(value = "download")
    public void download( HttpServletResponse response) throws IOException {
//        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        HttpServletResponse response = requestAttributes.getResponse();
        String filename ="CB202307110110003-1.xlsx";
        // 设置信息给客户端不解析
//        String type = new MimetypesFileTypeMap().getContentType(filename)+";charset=UTF-8";
        String type = new MimetypesFileTypeMap().getContentType(filename);
        response.setCharacterEncoding("utf-8");
//        response.setHeader("Content-Type","text/html;charset=UTF-8");
        response.setHeader("Content-type",type);
        response.setHeader("Content-type001",type);
        response.setHeader("Content-Language", "UTF-8");
        // 设置扩展头，当Content-Type 的类型为要下载的类型时 , 这个信息头会告诉浏览器这个文件的名字和类型。
        response.setHeader("Content-Disposition", "attachment;filename=" + "CB202307110110003-1.xlsx");

//        FileUtil.download02("D:\\Desktop\\"+filename, response);
        FileUtil.download("D:\\Desktop\\test\\"+filename, response);
//        FileUtil.download03("D:\\Desktop\\"+filename, response);
    }

    /**
     * map导入
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @RequestMapping("/uploadMap")
    public String uploadMap(HttpServletRequest request, HttpServletResponse response) throws Exception {

        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        List<Map<String, Object>> list = null;
        Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
        for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
            List<Map<String, Object>> listAsds = null;
            try{
                MultipartFile file = entity.getValue();// 获取上传文件对象
                ImportParams params = new ImportParams();
//                params.setDataHandler(new MapImportHandler());
//                params.setVerifyHandler(new ExcelVerifyHandlerImpl());
                params.setTitleRows(2);
//            params.setHeadRows(1);
//                params.setNeedSave(true);
//                ExcelImportResult<List<Map<String, Object>>> result
                listAsds =  ExcelImportUtil.importExcel(file.getInputStream(), Map.class, params);
                System.out.println(JSON.toJSONString(listAsds));
            }catch (Exception e){
                e.printStackTrace();
            }

        }
        return "";
    }

    //    下载图片
    @PostMapping("download01")
    public ResponseEntity<InputStreamResource> Test02(HttpServletResponse response) throws Exception {
        String filename ="压测接口.txt";
        //下面两行，初始化hdfs配置连接
        String type = new MimetypesFileTypeMap().getContentType(filename);
        // 设置contenttype，即告诉客户端所发送的数据属于什么类型
//        response.setHeader("Content-type",type);
//        // 设置编码
        String hehe = new String(filename.getBytes("utf-8"), "iso-8859-1");
//        // 设置扩展头，当Content-Type 的类型为要下载的类型时 , 这个信息头会告诉浏览器这个文件的名字和类型。
//        response.setHeader("Content-Disposition", "attachment;filename=" + hehe);
        FileInputStream inputStream = new FileInputStream("D:\\Desktop1\\压测接口.txt" );
        byte[] testBytes = new byte[inputStream.available()];
        HttpHeaders headers = new HttpHeaders();
        headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
        headers.add("Content-Disposition", "attachment;filename=" + hehe);
        headers.add("Content-type",type);
        headers.add("Pragma", "no-cache");
        headers.add("Expires", "0");
        headers.add("Content-Language", "UTF-8");
        headers.add("eoor", "傻逼");

//        FileUtil.download(filename, response);
        return ResponseEntity.ok().headers(headers).contentLength(testBytes.length)
                .contentType(MediaType.parseMediaType("application/octet-stream")).body(new InputStreamResource(inputStream));
    }

    /**
     * 本地文件下载
     * @param request
     * @param response
     */
    @RequestMapping("/downloadFileLocal")
    public void downloadFileLocal(HttpServletRequest request, HttpServletResponse response){
        //文件本地位置
        String filePath="D:\\Desktop\\test\\";
        String fileName="CB202307110110003-1.xlsx";

        File file = new File(filePath);
        FileUtiles.downloadFile(file,request,response,fileName);
    }

    public static final String FILE_FOLDER = "/static/files/";
    @PostMapping("/upload")
    public String upload(MultipartFile multipartFile) throws IOException {

        // 文件原名称
        String oldName = multipartFile.getOriginalFilename();
        // 文件后缀
        String extension = "." + FilenameUtils.getExtension(multipartFile.getOriginalFilename());
        // 新文件名
        String newName = UUID.randomUUID().toString().replace("-", "").substring(8) + extension;
        // 文件尺寸
        long size = multipartFile.getSize();
        // 文件类型
        String type = multipartFile.getContentType();
        // 获取编译后项目根路径
        String basePath = ResourceUtils.getURL("classpath:").getPath()+"/test";
        // 按照时间分类
        String timePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        // 根路径/static/files/20xx-xx-xx/
        String finalPath = basePath + FILE_FOLDER + timePath;
        // 创建文件夹
        File finalFile = new File(finalPath);
        if (!finalFile.exists()) {
            finalFile.mkdirs();
        }
        // 上传文件: 核心代码
        multipartFile.transferTo(new File(finalFile, newName));

        return "redirect:/index";
    }

    @PostMapping("/downloadid")
    public void download(Integer id, HttpServletResponse response) throws IOException {
        // 获取项目根路径
//        String realpath = ResourceUtils.getURL("classpath:").getPath() ;
        String realpath = "D:\\Desktop\\" ;
        // 获得文件输入流
        FileInputStream inputStream = new FileInputStream(new File(realpath, "mq.txt"));
        // 设置文件下载方式：附件下载
        response.setHeader("content-disposition", "attachment;fileName=" + URLEncoder.encode("mq.txt", "UTF-8"));
        // 获取响应输出流
        ServletOutputStream outputStream = response.getOutputStream();
        // 下载文件
        IOUtils.copy(inputStream, outputStream);
        IOUtils.closeQuietly(inputStream);
        IOUtils.closeQuietly(outputStream);
    }

    // 删除文件
    @GetMapping("/delete")
    public String delete(int id) throws FileNotFoundException {
        // 删除文件
        String finalPath = ResourceUtils.getURL("classpath:").getPath() ;
        File file = new File(finalPath, "FileName");
        if (file.exists()) {
            file.delete();
        }
        return "redirect:/index";
    }

        private final static String utf8 = "utf-8";

    /**
     * 分片上传
     * @param request
     * @param response
     * @throws Exception
     */
        @RequestMapping("/up")
        @ResponseBody
        public void upload(HttpServletRequest request, HttpServletResponse response) throws Exception {
            response.setCharacterEncoding(utf8);
            //长传时候会有多个分片，需要记录当前为那个分片
            Integer schunk = null;
            //总分片数
            Integer schunks = null;
            //名字
            String name = null;
            //文件目录
            String path = "D:\\file";
            BufferedOutputStream os = null;
            try {
                //设置缓冲区大小  先读到内存里在从内存写
                DiskFileItemFactory factory = new DiskFileItemFactory();
                factory.setSizeThreshold(1024);
                factory.setRepository(new File(path));
                //解析
                ServletFileUpload upload = new ServletFileUpload(factory);
                //设置单个大小与最大大小
                upload.setFileSizeMax(5l*1024l*1024l*1024l);
                upload.setSizeMax(10l*1024l*1024l*1024l);
                List<FileItem> items = upload.parseRequest(request);
                for (FileItem item : items){
                    if (item.isFormField()){
                        //获取分片数赋值给遍量
                        if ("chunk".equals(item.getFieldName())){
                            schunk = Integer.parseInt(item.getString(utf8));
                        }
                        if ("chunks".equals(item.getFieldName())){
                            schunks = Integer.parseInt(item.getString(utf8));
                        }
                        if ("name".equals(item.getFieldName())){
                            name = item.getString(utf8);
                        }
                    }
                }
                //取出文件基本信息后
                for (FileItem item : items){
                    if (!item.isFormField()){
                        //有分片需要临时目录
                        String temFileName = name;
                        if (name != null){
                            if (schunk != null){
                                temFileName = schunk+"_"+name;
                            }
                            //判断文件是否存在
                            File temfile = new File(path, temFileName);
                            //断点续传  判断文件是否存在，若存在则不传
                            if (!temfile.exists()){
                                item.write(temfile);
                            }
                        }
                    }
                }
                //文件合并  当前分片为最后一个就合并
                if (schunk != null && schunk.intValue()== schunks.intValue()-1){
                    File tempFile = new File(path, name);
                    os = new BufferedOutputStream(new FileOutputStream(tempFile));
                    //根据之前命名规则找到所有分片
                    for (int i = 0; i < schunks; i++) {
                        File file = new File(path, i + "_" + name);
                        //并发情况 需要判断所有  因为可能最后一个分片传完，之前有的还没传完
                        while (!file.exists()){
                            //不存在休眠100毫秒后在从新判断
                            Thread.sleep(100);
                        }
                        //分片存在  读入数组中
                        byte[] bytes = FileUtils.readFileToByteArray(file);
                        os.write(bytes);
                        os.flush();
                        file.delete();
                    }
                    os.flush();
                }
                response.getWriter().write("上传成功");
            }finally {
                try {
                    if (os != null){
                        os.close();
                    }
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }

    /**
     * 分片下载
     * @param request
     * @param response
     * @throws IOException
     */
    @RequestMapping("/down")
    public void downLoadFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setCharacterEncoding(utf8);
        //定义文件路径
        File file = new File("D:\\File\\a.mp4");
        InputStream is = null;
        OutputStream os = null;
        try {
            //分片下载
            long fSize = file.length();//获取长度
            response.setContentType("application/x-download");
            String fileName = URLEncoder.encode(file.getName(),utf8);
            response.addHeader("Content-Disposition","attachment;filename="+fileName);
            //根据前端传来的Range  判断支不支持分片下载
            response.setHeader("Accept-Range","bytes");
            //获取文件大小
            response.setHeader("fSize",String.valueOf(fSize));
            response.setHeader("fName",fileName);
            //定义断点
            long pos = 0,last = fSize-1,sum = 0;
            //判断前端需不需要分片下载
            if (null != request.getHeader("Range")){
                response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                String numRange = request.getHeader("Range").replaceAll("bytes=","");
                String[] strRange = numRange.split("-");
                if (strRange.length == 2){
                    pos = Long.parseLong(strRange[0].trim());
                    last = Long.parseLong(strRange[1].trim());
                    //若结束字节超出文件大小 取文件大小
                    if (last>fSize-1){
                        last = fSize-1;
                    }
                }else {
                    //若只给一个长度  开始位置一直到结束
                    pos = Long.parseLong(numRange.replaceAll("-","").trim());
                }
            }
            long rangeLenght = last-pos+1;
            String contentRange = new StringBuffer("bytes").append(pos).append("-").append(last).append("/").append(fSize).toString();
            response.setHeader("Content-Range",contentRange);
            response.setHeader("Content-Lenght",String.valueOf(rangeLenght));
            os = new BufferedOutputStream(response.getOutputStream());
            is = new BufferedInputStream(new FileInputStream(file));
            is.skip(pos);//跳过已读的文件
            byte[] buffer = new byte[1024];
            int lenght = 0;
            //相等证明读完
            while (sum < rangeLenght){
                lenght = is.read(buffer,0, (rangeLenght-sum)<=buffer.length? (int) (rangeLenght - sum) :buffer.length);
                sum = sum+lenght;
                os.write(buffer,0,lenght);

            }
            System.out.println("下载完成");
        }finally {
            if (is!= null){
                is.close();
            }
            if (os!=null){
                os.close();
            }
        }
    }

    private static final String FILE_STORAGE_LOCATION = "/path/to/your/files"; // 文件存储位置

    @GetMapping("/downloadFile03/{filename:.+}")
    public ResponseEntity<Resource> downloadFile03(@PathVariable String filename) {
        Path filePath = Paths.get(FILE_STORAGE_LOCATION, filename);
        File file = filePath.toFile();

        try {
            Resource fileResource = new InputStreamResource(new FileInputStream(file));

            return ResponseEntity.ok()
                    .contentType(MediaType.parseMediaType(Files.probeContentType(filePath)))
                    .header("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"")
                    .body(fileResource);
        } catch (IOException e) {
            e.printStackTrace();
            return ResponseEntity.notFound().build();
        }
    }
    @GetMapping("/downloadFile01/{filename:.+}")
    public void downloadFile01(@PathVariable String filename, HttpServletResponse response) {
        Path filePath = Paths.get(FILE_STORAGE_LOCATION, filename);
        File file = filePath.toFile();

        try {
            if (!file.exists()) {
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return;
            }

            // 设置响应头信息
            response.setContentType(Files.probeContentType(filePath));
            response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");

            try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
                 OutputStream out = response.getOutputStream()) {

                byte[] buffer = new byte[4096];
                int bytesRead;

                // 将文件内容写入响应输出流
                while ((bytesRead = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesRead);
                }

                out.flush();
            }

        } catch (IOException e) {
            e.printStackTrace();
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }


    @GetMapping("/downloadFile06")
    public ResponseEntity<?> downloadFile06(String fileName) {
        try {
            // 假设文件路径是硬编码的，实际应用中可能需要从数据库或配置文件中获取
            Path filePath = Paths.get("path/to/your/files/" + fileName);
            byte[] fileContent = Files.readAllBytes(filePath);

            HttpHeaders headers = new HttpHeaders();
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"");
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

            Resource resource = new ByteArrayResource(fileContent);
            return ResponseEntity.ok()
                    .headers(headers)
                    .body(resource);

        } catch (IOException e) {
            e.printStackTrace();
            // 处理异常，返回JSON错误信息
            Map<String, String> errorResponse = new HashMap<>();
            errorResponse.put("error", "Failed to download file");
            errorResponse.put("message", e.getMessage());

//            return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
            return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    // DTO类用于接收请求体中的文件名
    public static class FileDownloadRequest {
        private String fileName;

        // Getter和Setter方法
        public String getFileName() {
            return fileName;
        }

        public void setFileName(String fileName) {
            this.fileName = fileName;
        }
    }

    @PostMapping("/downloadFile07")
    public ResponseEntity<?> downloadFile07(@RequestBody FileDownloadRequest param) {
        try {
            Path filePath = Paths.get("path/to/your/files/" + param.getFileName());
            byte[] fileContent = Files.readAllBytes(filePath);

            // 设置响应头以指示这是一个文件下载
            Map<String, String> headers = new HashMap<>();
            headers.put("Content-Disposition", "attachment; filename=\"" + filePath.getFileName() + "\"");
            headers.put("Content-Type", "application/octet-stream");

            // 如果一切正常，返回文件内容
            return ResponseEntity.ok()
                    .headers(headersToHttpHeaders(headers))
                    .body(fileContent);

        } catch (IOException e) {
            e.printStackTrace();
            // 创建错误信息的Map
            Map<String, Object> errorDetails = new HashMap<>();
            errorDetails.put("timestamp", System.currentTimeMillis());
            errorDetails.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
            errorDetails.put("error", "Internal Server Error");
            errorDetails.put("message", e.getMessage());

            // 返回包含错误信息的ResponseEntity，并设置Content-Type为application/json
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(errorDetails);
        }
    }

    // 辅助方法，将Map<String, String>转换为HttpHeaders
    private HttpHeaders headersToHttpHeaders(Map<String, String> headers) {
        HttpHeaders httpHeaders = new HttpHeaders();
        headers.forEach(httpHeaders::add);
        return httpHeaders;
    }

}
